#include <iostream>
#include <fstream>
#include <string>

#include "avr-spec.h"
#include "avr-builder.h"
#include "avr.h"
#include "bit-field.h"
#include "debug-shell.h"

using namespace std;

void print_usage (const char* name, const char* message, const char* arg)
{
  if (message) {
    cout << message;

    if (arg)
      cout << ": " << arg;

    cout << endl << endl;
  }

  cout << "Usage: " << name << " [options] <program>" << endl << endl;
  cout << "Options:" << endl;
  cout << "  -p <part>    set part to simulate, defaults to atmega32" << endl;
  cout << "  -s <file>    set spec file" << endl;
  cout << "  -d           enter debug shell" << endl;
}

int main (int argc, char** argv)
{
  string spec_file = "specs.conf";
  string part = "atmega32";
  bool debugs = false;

  if (argc < 2) {
    print_usage (argv[0], 0, 0);
    return 0;
  }

  for (int i = 1; i < argc - 1; ++i) {
    string cur = argv[i];

    if (cur == "-p") {
      if (i >= argc - 2) {
        print_usage (argv[0], 0, 0);
        return 1;
      }

      part = argv[++i];
    }
    else if (cur == "-s") {
      if (i >= argc - 2) {
        print_usage (argv[0], "No spec file given", 0);
        return 1;
      }

      spec_file = argv[++i];
    }
    else if (cur == "-d") {
      debugs = true;
    }
    else {
      print_usage (argv[0], "Invalid option", argv[i]);
      return 1;
    }
  }

  string program = argv[argc - 1];

  ifstream is (spec_file.c_str ());
  if (!is.is_open ()) {
    cout << "Unable to open spec file: " << spec_file << endl;
    return 1;
  }

  AVRSpec spec (is, part);

  switch (spec.error ()) {
    case AVRSpec::PartNotFound:
      cout << "Invalid part: " << part << endl;
      return 1;

    case AVRSpec::FileFormatError:
      cout << "Error in spec file on line " << spec.line () << endl;
      return 1;


    case AVRSpec::InvalidOption:
      cout << "Invalid option in spec file on line " << spec.line () << endl;
      return 1;

    case AVRSpec::InvalidOptionValue:
      cout << "Invalid option value in spec file on line " << spec.line ()
           << endl;
      return 1;

    default:
      break;
  }

  AVR avr;

  AVRBuilder::build (spec, avr);

  ifstream ps (program.c_str ());
  if (!ps.is_open ()) {
    cout << "Unable to open program file: " << program << endl;
    return 1;
  }

  if (debugs) {
    DebugShell dshell (avr);

    dshell.run ();
  }
  else {
    avr.load_flash (ps);
    avr.reset ();
    avr.dump_core_status (cout);
    cout << endl;
    avr.step ();
    avr.dump_core_status (cout);
  }

  ps.close ();
  is.close ();
  
  return 0;
}
